<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
    <style>
        #app {
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        canvas {
            border: 1px solid black;
            margin-top: 20px;
        }

        .form-list {
            display: flex;
            flex-direction: column;
        }

        .arguments-intersection {
            display: flex;
        }

        .arguments-intersection-title {
            width: 131.863px;
        }
    </style>
</head>

<body>

    <div id="app">
        <div class="form-list">
            <div class="arguments-intersection">
                <div class="arguments-intersection-title">射线起点:</div>
                <input v-model.number="rayOrigin[0]" type="number">,
                <input v-model.number="rayOrigin[1]" type="number">
            </div>
            <div class="arguments-intersection">
                <div class="arguments-intersection-title">射线方向（向量）:</div>
                <input v-model.number="rayDirection[0]" type="number">,
                <input v-model.number="rayDirection[1]" type="number">
            </div>
            <div class="arguments-intersection">

                <div class="arguments-intersection-title">圆心:</div>
                <input v-model.number="circleCenter[0]" type="number">,
                <input v-model.number="circleCenter[1]" type="number">
            </div>
            <div class="arguments-intersection">
                <div class="arguments-intersection-title">半径:</div>
                <input v-model.number="radius" type="number">
            </div>
        </div>
        <button @click="calculateIntersections">Calculate Intersections</button>
        <div>
            交点: {{ intersections }}
        </div>


        <!-- Canvas for drawing -->
        <canvas ref="drawingCanvas" width="400" height="400"></canvas>
    </div>

    <script>
        function rayCircleIntersection(rayOrigin, rayDirection, circleCenter, radius) {
            // 解析输入参数
            const [x0, y0] = rayOrigin;
            const [dx, dy] = rayDirection;
            const [h, k] = circleCenter;
            const r = radius;

            // 计算二次方程的系数
            const a = dx * dx + dy * dy;
            const b = 2 * (dx * (x0 - h) + dy * (y0 - k));
            const c = (x0 - h) * (x0 - h) + (y0 - k) * (y0 - k) - r * r;

            // 计算判别式
            const discriminant = b * b - 4 * a * c;

            if (discriminant < 0) {
                return []; // 没有交点
            } else if (discriminant === 0) {
                // 只有一个交点（相切）
                const t = -b / (2 * a);
                if (t >= 0) { // 只接受非负的t值
                    return [[x0 + t * dx, y0 + t * dy]];
                } else {
                    return [];
                }
            } else {
                // 两个交点
                const sqrtDiscriminant = Math.sqrt(discriminant);
                const t1 = (-b + sqrtDiscriminant) / (2 * a);
                const t2 = (-b - sqrtDiscriminant) / (2 * a);

                const intersections = [];
                if (t1 >= 0) {
                    intersections.push([x0 + t1 * dx, y0 + t1 * dy]);
                }
                if (t2 >= 0 && !isClose(t2, t1)) { // 排除重合的点
                    intersections.push([x0 + t2 * dx, y0 + t2 * dy]);
                }

                return intersections;
            }
        }

        // 辅助函数：判断两个浮点数是否接近
        function isClose(a, b, relativeTolerance = 1e-9, absoluteTolerance = 1e-9) {
            return Math.abs(a - b) <= Math.max(relativeTolerance * Math.max(Math.abs(a), Math.abs(b)), absoluteTolerance);
        }

        new Vue({
            el: '#app',
            data() {
                return {
                    rayOrigin: [-110, -45], //起点
                    rayDirection: [3, 3], //方向向量
                    circleCenter: [5, 5], //圆心
                    radius: 110, //半径
                    intersections: [], //交点
                    canvasContext: null,
                    canvasSize: 400 // Assuming square canvas
                };
            },
            methods: {
                calculateIntersections() {
                    this.intersections = rayCircleIntersection(this.rayOrigin, this.rayDirection, this.circleCenter, this.radius);
                    this.draw();
                },
                draw() {
                    const canvas = this.$refs.drawingCanvas;
                    this.canvasContext = canvas.getContext('2d');

                    // Clear the canvas
                    this.canvasContext.clearRect(0, 0, this.canvasSize, this.canvasSize);

                    // Draw the circle
                    this.canvasContext.beginPath();
                    this.canvasContext.arc(
                        this.circleCenter[0] + this.canvasSize / 2, // Adjust for centering on canvas
                        this.canvasSize / 2 - this.circleCenter[1], // Invert Y to match canvas coordinate system
                        this.radius,
                        0,
                        2 * Math.PI
                    );
                    this.canvasContext.stroke();

                    // Draw the ray
                    const endX = this.rayOrigin[0] + this.rayDirection[0] * 100; // Extend ray for visualization
                    const endY = this.rayOrigin[1] + this.rayDirection[1] * 100;
                    this.canvasContext.beginPath();
                    this.canvasContext.moveTo(this.rayOrigin[0] + this.canvasSize / 2, this.canvasSize / 2 - this.rayOrigin[1]);
                    this.canvasContext.lineTo(endX + this.canvasSize / 2, this.canvasSize / 2 - endY);
                    this.canvasContext.stroke();

                    // Draw intersection points
                    this.intersections.forEach(([x, y]) => {
                        this.canvasContext.beginPath();
                        this.canvasContext.arc(x + this.canvasSize / 2, this.canvasSize / 2 - y, 3, 0, 2 * Math.PI);
                        this.canvasContext.fillStyle = 'red';
                        this.canvasContext.fill();
                    });
                },
                mounted() {
                    // Initialize drawing when component is mounted
                    this.calculateIntersections();
                }
            }
        });
    </script>

</body>

</html>